/*********************************************************************************
 *      Copyright:  (C) 2025 lijiahui<2199250859@qq.com>
 *                  All rights reserved.
 *
 *       Filename:  pack.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(21/04/25)
 *         Author:  lijiahui <2199250859@qq.com>
 *      ChangeLog:  1, Release initial version on "21/04/25 11:47:24"
 *                 
 ********************************************************************************/

#include "get_temp.h"
#include "pack.h"

int pack_data( pack_t *pack )
{
        char    time[20];
        float   temp;
        char    devid[20];
        int     rv = -1;


        if( !pack )
        {
                log_error("Invalid input arguments.\n");
                return -1;
        }

        //get device id
        rv = get_devid(devid,sizeof(devid));
        if(rv == 0)
        {
                snprintf(pack->id, sizeof(pack->id), "%s", devid);
        }
        else
        {
                log_error("can not get device id \n");
                return -2;
        }

        //get time
        rv = get_time(time);
        if(rv == 0)
        {
                snprintf(pack->systime, sizeof(pack->systime), "%s", time);
        }
        else
        {
                log_error("can not get time\n");
                return -3;
        }

        rv = ds18b20_get_temperature(&temp);
        if(rv)
        {
                snprintf(pack->temp, sizeof(pack->temp), "%f", temp);
        }
        else
        {
                log_error("get temperature error\n");
                return -4;
        }

        
		return 0;

}


int pack_data_segmented( pack_t *pack, uint8_t *pack_buf, int size )
{
	char	*buf = (char *)pack_buf;
	
	
	if( !pack || !buf || size<=0)
	{
		log_error("Invalid input arguments.\n");
		return -1;
	}

	memset(buf, 0, size);
	snprintf(buf, size, "%s+%s+%s", pack->systime, pack->id, pack->temp);

	//返回实际长度
	return strlen(buf);
	
}

int pack_json_message( pack_t *pack, uint8_t *pack_buf, int size)
{
	char *buf = (char *)pack_buf;

	if( !pack || !buf ||size<=0 )
	{
		log_error("INvaild input arguments.\n");
		return -1;
	}

	memset(buf, 0, sizeof(buf));
	snprintf(buf, size, "{\"time\":\"%s\", \"device_id\":\"%s\", \"temperature\":\"%s\"}",
			pack->systime, pack->id, pack->temp);

	return strlen(buf);
}

/* CRC校验（本质：发送方将数据视为多项式，除以CRC多项式，余数作为校验值附加到数据末尾。接收方重复计算，若余数非零则说明传输错误），使用0x1021（转换为二进制可得x^16+x^12+x^5+1）
JSON通常无需CRC校验，TLV必须使用CRC校验，因为二进制协议缺乏自校验能力
*/

#define CRC16_ITU_T_POLY 0x1021
static uint16_t crc_itu_t(const uint8_t *data, size_t length)
{
	uint16_t	crc = 0xFFFF;//crc计算的结果,初始值为0xFFFF
	size_t		i, j;

	for(i=0; i<length; i++)
	{
		crc ^= ((uint16_t)data[i] << 8);//将数据字节放到CRC的高八位，并与当前的crc做异或更新crc寄存器的值

		for(j=0; j<8; j++)
		{
			if(crc & 0x8000)//检查最高位是否为1
			{
				crc = (crc <<1 ) ^ CRC16_ITU_T_POLY;//左移一位做异或
			}
			else
			{
				crc <<= 1;
			}
		}
	}

	return crc;//返回16位校验码
}


//小端字节序转为大端字节序
uint16_t to_big_endian(uint16_t num)
{
	return ((num & 0x00FF) << 8) | ((num & 0xFF00) >> 8);//低八位和高八位交换
}

/* 
 *TLV format：Header(2B) | Tag(1B) | Length(2B) | Value(nB) | CRC16(2B)
*/

int pack_tlv_message(pack_t *pack, uint8_t *pack_buf, int size)
{
	int 		year, month, day, hour, min, sec;
	int			offset = 0;
	uint16_t	crc;
	float		temperature = atof(pack->temp);

	if( !pack || !pack_buf || size<TLV_MINSIZE )
	{
		log_error("Invalid input arguments.\n");
		return -1;
	}

	//写入固定的协议头（Header），并确保为大端字节序
	*(uint16_t *)pack_buf = to_big_endian(TLV_HEADER);
	offset += 2;//写入Header占2字节，故偏移量需+2，指向下一个字段（Tag）的位置

	//写入Tag
	pack_buf[offset++] = TAG_TEMPERATURE;//标记数据类型为温度

	//预留length位置，跳过length，稍后回填(因为length是value的字节数，需先计算完value后才能确定)
	offset +=2;


	//写入value（id+time+温度）

	//解析字符串时间	
	if (sscanf(pack->systime, "%d-%d-%d %d:%d:%d", 
				&year, &month, &day, &hour, &min, &sec) != 6) 
	{
        log_error("Invalid time format: %s\n", pack->systime);
        return -2;
    }

	//写入二进制时间（6 bytes）
	pack_buf[offset++] = (uint8_t)(year - 2000);//年
	pack_buf[offset++] = (uint8_t)month;
	pack_buf[offset++] = (uint8_t)day;
	pack_buf[offset++] = (uint8_t)hour;
	pack_buf[offset++] = (uint8_t)min;
	pack_buf[offset++] = (uint8_t)sec;

	//8 bytes device id
	memset(pack_buf + offset, 0, DEVID_LEN);
	strncpy((char *)(pack_buf + offset), pack->id, DEVID_LEN);
	offset += DEVID_LEN;

	//2 bytes temperature value	
	pack_buf[offset++] = (int)temperature;//整数部分
	pack_buf[offset++] = (((int)(temperature*100))%100);//小数部分

	//回填length（2B）
	uint16_t len_be = to_big_endian(offset - 5);
	memcpy(pack_buf + 3, &len_be, sizeof(len_be));//在TLV协议包的指定位置写入一个16位的字节数（即length字段的值：16）

	//crc校验码
	crc = crc_itu_t(pack_buf, offset);//计算校验码
	uint16_t crc_be = to_big_endian(crc);
	memcpy(pack_buf+offset, &crc_be, sizeof(crc_be)); //写入TLV包的末尾（value后）
	offset +=2;//更新偏移量


	return offset;	
}




